home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / UCRASM25.ARJ / FILEIO.ASM < prev    next >
Assembly Source File  |  1991-10-12  |  18KB  |  639 lines

  1. ;******************************************************************************
  2. ;
  3. ;                       New routines and functions
  4. ;                         for the Stdlib package
  5. ;
  6. ;               Featuring:
  7. ;                       - Opening and closing files
  8. ;                       - Creating new files
  9. ;                       - Blocked I/O:
  10. ;                               -Reading from files using getc
  11. ;                               -Writing to files using putc and puts
  12. ;                - File-Buffer Flushing
  13. ;            - File Seeking
  14. ;            - File Deleting
  15. ;            - File Renaming
  16. ;
  17. ;******************************************************************************
  18. ;
  19. ;               Written by:
  20. ;
  21. ;                       Mark Radleigh
  22. ;                           &
  23. ;                       Brian Harvey
  24. ;
  25. ;            (CS 13)
  26. ;
  27. ;******************************************************************************
  28. ;
  29. ;
  30. ;******************************************************************************
  31. ;        These externs are needed for this demonstration program.
  32. ;******************************************************************************
  33. ;
  34. ;
  35. StdGrp        group    StdLib, StdData
  36. ;
  37. StdData        segment    para public 'sldata'
  38. ;
  39. ; Some useful constants:
  40. ;
  41. cr              equ        13
  42. lf              equ        10
  43. eos             equ        0
  44. EOF             equ     04h
  45. ;
  46. true            equ     1
  47. false           equ     0
  48. NIL        equ    0
  49. ;
  50. ;******************************************************************************
  51. ;    The following is a Struct that contains all necessary information to
  52. ;    handle multiple files opened or closed.
  53. ;******************************************************************************
  54. ;
  55. ftype        struc
  56. fbuffptr    dd    ?        ;Buffer pointer that holds the Data
  57. fhandle        dw    ?        ;DOS FileHandle
  58. findex        dw    ?        ;Index into Buffer
  59. fcounter    dw    ?
  60. fflush_flag    db    ?        ;Says if you can flush the buffer or not
  61. ftype        ends
  62. ;
  63. ;******************************************************************************
  64. ;    The Folowing declares neccessary variables for the library.
  65. ;    FILES is the base struct for the alloted number of files opened.
  66. ;******************************************************************************
  67. ;
  68. FILES        ftype    10 dup (<NIL,NIL,NIL,NIL,NIL>)
  69. Old_out_addrss  dd      0               ;Keep the old address of output
  70. Old_in_addrss   dd      0               ;Keep the old address of input
  71. current_fwrite    dw    ?        ;Keep the current index of writing
  72. current_fread    dw    ?        ;Keep the current index of reading
  73. ;
  74. ;******************************************************************************
  75. ;    The following are variables used by the demonstration program.
  76. ;******************************************************************************
  77. ;
  78. fileptr        dw    ?
  79. fileptr2    dw    ?
  80. fileptr3    dw    ?
  81. ;
  82. StdData        ends
  83. ;
  84. stdlib          segment para public 'slcode'
  85.         assume  cs:StdGrp, ds:nothing
  86. ;
  87.         extrn    sl_SetOutAdrs:far, sl_GetOutAdrs:far
  88.         extrn    sl_SetInAdrs:far, sl_GetInAdrs:far
  89.         extrn    sl_malloc:far, sl_free:far
  90. ;
  91. ;
  92. ;----------------------------------------------------------------------------
  93. ;  Fcreate -> A procedure that creates a file for writing (Deletes any previous
  94. ;          file of the same name.)
  95. ;    INPUT-> ES:DI Contains the address of the filename for the new file.
  96. ;    OUTPUT-> CARRY Flag is set if error occurs.
  97. ;    OUTPUT-> AX contains the error code if carry flag is set, else AX
  98. ;         contains the STDLIB FileHandle.
  99. ;
  100.         public    sl_fcreate
  101. sl_fcreate    proc    far
  102.         push    ds
  103.         push    dx
  104.         push    cx
  105.         push    si
  106. ;
  107. fcreate_read:    xor     si, si
  108. fcreate_test:    cmp    word ptr StdGrp:FILES[si].fbuffptr + 2, NIL
  109.         jne    fcreate_test2
  110.         cmp    word ptr StdGrp:FILES[si].fbuffptr, NIL
  111.         je    Fcreate_file
  112. fcreate_test2:     add    si, size ftype
  113.         cmp    si, length FILES * size ftype
  114.         jbe    fcreate_test
  115.         stc
  116.         mov    ax, 10        ;No more file buffer ->error
  117.         jmp    fcreate_done
  118. Fcreate_file:
  119.         mov    dx, es
  120.         mov    ds, dx        ;get the segment adrs ready
  121.                     ;for DOS
  122.         mov    dx, di        ;get the offset adrs ready
  123.                     ;for DOS
  124.         mov    ah, 3ch        ;command for CREATE
  125.         mov    cx, 0        ;Tells DOS file is ARCHIVED.
  126.         int    21h
  127.         jc    fcreate_done
  128.         mov    StdGrp:FILES[si].fflush_flag, true     ;So we can flush
  129.                             ;the buffer!
  130.         mov    StdGrp:FILES[si].fhandle, ax
  131.         mov    ax, si
  132.         call    fmalloc_this        ;Malloc space for the buffer
  133. ;
  134. fcreate_done:    pop    si
  135.         pop    cx
  136.         pop    dx
  137.         pop    ds
  138.         ret
  139. sl_fcreate    endp
  140. ;
  141. ;-----------------------------------------------------------------------------
  142. ;  Fopen -> A procedure that opens a file for reading/writing.
  143. ;    INPUT-> ES:DI Contains the address of the filename for the new file.
  144. ;    OUTPUT-> CARRY Flag is set if error occurs.
  145. ;    OUTPUT-> AX contains the error code if carry flag is set, else AX
  146. ;         contains the STDLIB FileHandle.
  147. ;
  148. ;
  149.         public    sl_fopen
  150. sl_fopen    proc    far
  151.         push    cx
  152.         push    dx
  153.         push    si
  154.         push    di
  155.         push     ds
  156.         push    es
  157.         cmp    al, 1        ;Want to open for writing?
  158.         je    fopen_write
  159.         cmp    al, 0        ;Want to open for reading?
  160.         je    fopen_read
  161.         stc
  162.         mov    ax, 5        ;Incorrect sub function passed in al
  163.         jmp    fopen_done
  164. ;
  165. ;--------------------------------------------------
  166. ;    Here we open the file for reading..
  167. ;    We first test to see if there is an open buffer, then we open the file,
  168. ;    and if everything is ok, we Malloc space for the buffer.
  169. ;
  170. fopen_read:    xor     si, si
  171. fread_test:    cmp    word ptr StdGrp:FILES[si].fbuffptr + 2, NIL
  172.         jne    fread_test2
  173.         cmp    word ptr StdGrp:FILES[si].fbuffptr, NIL
  174.         je    Open_file_r
  175. fread_test2:     add    si, size ftype
  176.         cmp    si, length FILES * size ftype
  177.         jbe    fread_test
  178.         stc
  179.         mov    ax, 10            ;No more file buffers ->error
  180.         jmp    fopen_done
  181. ;
  182. Open_file_r:    mov    dx, es
  183.         mov    ds, dx
  184.         mov    dx, di
  185.         mov    ah, 3dh            ; Code for opening for reading
  186.         int     21h
  187.         jc    fopen_done
  188.         mov    StdGrp:FILES[si].fflush_flag, false    ;so we don't flush the
  189.                             ;buffer
  190.         mov        StdGrp:FILES[si].fhandle, ax
  191.         mov    ax, si
  192.         call    fmalloc_this        ;Get some buffer space
  193.         jmp    fopen_done
  194. ;
  195. ;--------------------------------------------------
  196. ;    Here we open the file for writing..
  197. ;    We first test to see if there is an open buffer, then we open the file,
  198. ;    and if everything is ok, we Malloc space for the buffer.
  199. ;
  200. fopen_write:     mov    si, 0
  201. fwrite_test:    cmp    word ptr StdGrp:FILES[si].fbuffptr + 2, NIL
  202.         jne    fwrite_test2
  203.         cmp    word ptr StdGrp:FILES[si].fbuffptr, NIL
  204.         je    Open_file_w
  205. ;
  206. fwrite_test2:     add    si, size ftype
  207.         cmp    si, length FILES * size ftype
  208.         jbe    fwrite_test
  209.         stc
  210.         mov    ax, 10
  211.         jmp    fopen_done
  212. ;
  213. Open_file_w:    mov    dx, es
  214.         mov    ds, dx
  215.         mov    dx, di
  216.         mov    ah, 3dh         ;Code for open for writing
  217.         int    21h
  218.         jc    fopen_done
  219.         mov    StdGrp:FILES[si].fflush_flag, true    ;So we do flush
  220.                             ;File buffer
  221.         mov    StdGrp:FILES[si].fhandle, ax
  222.         mov    ax, si
  223.         call    fmalloc_this        ;Get some buffer space!
  224. ;
  225. fopen_done:
  226.         pop    es
  227.         pop    ds
  228.         pop    di
  229.         pop    si
  230.         pop    dx
  231.         pop     cx
  232.         ret
  233. sl_fopen    endp
  234. ;
  235. ;*****************************************************************************
  236. ;    This small procedure Mallocs some space for our FILES buffer pointers,
  237. ;    so we can read in and write out blocks at a time.
  238. ;*****************************************************************************
  239. ;
  240. fmalloc_this     proc    near
  241.         mov    cx, 256
  242.         call    sl_malloc
  243.         jnc    FMalloc_it
  244.         mov    ax, 10
  245. FMalloc_it:
  246.         mov    bx, es
  247.         mov    word ptr StdGrp:FILES[si].fbuffptr + 2, bx
  248.         mov    word ptr StdGrp:FILES[si].fbuffptr, di
  249.         mov    StdGrp:FILES[si].findex, 0
  250.         ret
  251. fmalloc_this    endp
  252. ;
  253. ;
  254. ;---------------------------------------------------------------------------
  255. ;    Readon -> A procedure that changes the stdlib fuctions of character
  256. ;          and string input to a specified file.
  257. ;           -INPUT  -> AX contains the FileHandle variable.
  258. ;
  259.         public    sl_freadon
  260. sl_freadon    proc    far
  261.         push    es
  262.         push    di
  263.         push    bx
  264.                         ;The following cmp instructions
  265.                         ;See if we have already set
  266.                         ;READON
  267.         mov    bx, word ptr StdGrp:Old_in_addrss + 2
  268.         cmp    bx, 0
  269.         jne    freadon_done
  270.         mov    di, word ptr StdGrp:Old_in_addrss
  271.         cmp    di, 0
  272.         jne    freadon_done
  273. ;
  274.         mov    current_fread, ax
  275.         call    sl_GetInAdrs        ;Get the origonal input adrs
  276.         mov    bx, es
  277.         mov    word ptr StdGrp:Old_in_addrss + 2, bx
  278.         mov    word ptr StdGrp:Old_in_addrss, di
  279.         mov    bx, cs            ;Now we load in our fgetc
  280.         mov    es, bx
  281.         lea    di, sl_fgetc
  282.         call    sl_SetInAdrs        ;Set to the new input adrs
  283. freadon_done:
  284.         pop    bx
  285.         pop    di
  286.         pop    es
  287.         ret
  288. sl_freadon    endp
  289. ;
  290. ;---------------------------------------------------------------------------
  291. ;    Readoff -> A procedure that resets the stdlib functions of character
  292. ;          and string input.
  293. ;
  294.         public    sl_freadoff
  295. sl_freadoff    proc    far
  296.         push    es
  297.         push    di
  298.         les    di, StdGrp:Old_In_Addrss
  299.         call    sl_SetInAdrs
  300.         mov    word ptr StdGrp:Old_In_Addrss + 2, 0
  301.         mov    word ptr StdGrp:Old_In_Addrss, 0
  302.         pop    di
  303.         pop    es
  304.         ret
  305. sl_freadoff    endp
  306. ;
  307. ;----------------------------------------------------------------------------
  308. ;    Fgetc -> A procedure that is called whenever GETC is called and
  309. ;         READON was called previously.
  310. ;    NOTE:       CARRY FLAG IS SET and AX contains 8 if it encounters EOF.
  311. ;
  312.         public    sl_fgetc
  313. sl_fgetc    proc    far
  314.         push    si
  315.         push    di
  316.         push    ds
  317.         push    dx
  318.         push    cx
  319.         push    bx
  320. ;
  321.         mov    si, StdGrp:current_fread        ;Get the current
  322.                             ;Readfile index
  323.                             ;-----------------
  324.                             ;The following 4 instruc
  325.                             ;see if we have an
  326.                             ;available buffer...
  327.         cmp    word ptr StdGrp:FILES[si].fbuffptr + 2, NIL
  328.         jne    fgetc_getchar
  329.         cmp    word ptr StdGrp:FILES[si].fbuffptr, NIL
  330.         je    fgetc_nada
  331. fgetc_getchar:
  332.         cmp    StdGrp:FILES[si].fcounter, 0
  333.         je    fget_new_block
  334.         les    di, StdGrp:FILES[si].fbuffptr
  335.         add    di, StdGrp:FILES[si].findex
  336.         mov    al, es:[di]
  337.         inc    StdGrp:FILES[si].findex
  338.         dec    StdGrp:FILES[si].fcounter
  339.         jmp    fgetc_done
  340. fget_new_block:
  341.         lds    dx, StdGrp:FILES[si].fbuffptr
  342.         mov    bx, StdGrp:FILES[si].fhandle
  343.         mov    cx, 256
  344.         mov    ah, 3fh
  345.         int    21h
  346.         jc    fgetc_done
  347.         cmp    ax, 0            ;How many bytes did it read in?
  348.         je    EOF_error
  349.         mov    StdGrp:FILES[si].fcounter, ax
  350.         mov    StdGrp:FILES[si].findex, 0
  351.         jmp    fgetc_getchar
  352. fgetc_nada:    stc
  353.         mov    ax, 6            ;Invalid FileHandle
  354.                         ;(Just a safeguard)
  355. EOF_error:    stc
  356.         mov    ax, 8            ;End of file...
  357. ;
  358. fgetc_done:
  359.         pop    bx
  360.         pop    cx
  361.         pop    dx
  362.         pop    ds
  363.         pop    di
  364.         pop    si
  365.         ret
  366. sl_fgetc    endp
  367. ;
  368. ;
  369. ;
  370. ;------------------------------------------------------------------------------
  371. ;
  372. ;  Fclose - A procedure that closes an open file.
  373. ;         - INPUT -> AX contains the FileHandle variable of the file to close.
  374. ;         - OUTPUT -> Carry flag is set if error occurs.
  375. ;         - OUTPUT -> Ax contains 6 which is invalid file handle error (if
  376. ;                     carry flag is set) or AX contains 7 if trouble with FREE.
  377. ;
  378.         public  sl_fclose
  379. sl_fclose       proc    far
  380.         push    bx
  381.         push    si
  382.         mov    si, ax
  383.         mov     bx, StdGrp:FILES[si].fhandle   ;Move file handle into bx
  384.         cmp        StdGrp:FILES[si].fflush_flag, true
  385.         jne    fclose_it
  386.         call    far ptr sl_fflush        ;Make sure the buffer is empty
  387. fclose_it:
  388.         mov     ah, 3eh
  389.         int     21h
  390.         les    di, StdGrp:FILES[si].fbuffptr
  391.         call    sl_free            ;Conserve space!!!!!
  392.         jnc    fclose_cont
  393.         mov    ax, 7            ;Trouble with FREE
  394. fclose_cont:
  395.         mov    word ptr StdGrp:FILES[si].fbuffptr + 2, 0
  396.         mov    word ptr StdGrp:FILES[si].fbuffptr, 0
  397.         mov    StdGrp:FILES[si].findex, 0
  398.         mov    StdGrp:FILES[si].fcounter, 0
  399.  
  400.         pop    si
  401.         pop     bx
  402.         ret
  403. sl_fclose       endp
  404. ;
  405. ;
  406. ;------------------------------------------------------------------------------
  407. ;    Fflush -> A procedure that flushes the write buffer into the file.
  408. ;          Usefull if user didn't read in 256 blocks
  409. ;    INPUT ->  AX contains the STDLIB FileHandle
  410. ;
  411.         public  sl_fflush
  412. sl_fflush       proc    far
  413.         push    di
  414.         push    si
  415.         push    ds
  416.         push    dx
  417.         push    cx
  418.         push    bx
  419.         mov    di, ax
  420.         mov     si, StdGrp:FILES[di].findex
  421.         cmp     si, 0            ;Is the buffer empty?
  422.         je     fwbuffempty
  423.         mov     cx, si            ;The number of bytes to write
  424.         lds     dx, StdGrp:FILES[di].fbuffptr
  425.         mov     ah, 40h
  426.         int     21h
  427. fwbuffempty:
  428.         pop     bx
  429.         pop     cx
  430.         pop     dx
  431.         pop     ds
  432.         pop     si
  433.         pop    di
  434.         ret
  435. sl_fflush       endp
  436.  
  437. ;------------------------------------------------------------------------------
  438. ;
  439. ;  Writeon - A procedure that changes stdlib functions of character and string
  440. ;            output to a specified file instead.
  441. ;          -INPUT -> AX contains the FileHandle variable.
  442. ;
  443.         public  sl_fwriteon
  444. sl_fwriteon     proc    far
  445.         push    es
  446.         push    di
  447.         push    bx
  448.                         ;The following cmp instructions
  449.                         ;See if we have already set
  450.                         ;WRITEON
  451. ;
  452.         mov     bx, word ptr StdGrp:Old_out_addrss + 2
  453.         cmp    bx, 0
  454.         jne    fwriteon_done
  455.         mov     di, word ptr StdGrp:Old_out_addrss
  456.         cmp    di, 0
  457.         jne    fwriteon_done
  458. ;
  459.         mov    StdGrp:current_fwrite, ax
  460.         call    sl_GetOutAdrs       ;Get the original ouput address
  461.         mov     bx, es
  462.         mov     word ptr StdGrp:Old_out_addrss + 2, bx
  463.         mov     word ptr StdGrp:Old_out_addrss, di
  464.         mov     bx, cs            ;Load in our fputc
  465.         mov     es, bx
  466.         lea     di, StdGrp:sl_fputc
  467.         call    sl_SetOutAdrs       ;sets the ouput address to file
  468. fwriteon_done:
  469.         pop     bx
  470.         pop     di
  471.         pop     es
  472.         ret
  473. sl_fwriteon     endp
  474. ;
  475. ;
  476. ;------------------------------------------------------------------------------
  477. ;    Fputc -> A procedure that is called whenever PUTC is called and
  478. ;         WRITEON was called previously.
  479. ;
  480.         public    sl_fputc
  481. sl_fputc    proc    far
  482.         push    si
  483.         push    di
  484.         push    ds
  485.         push    dx
  486.         push    cx
  487.         push    bx
  488. ;
  489.         mov    si, StdGrp:current_fwrite    ;gets the current index of FILES
  490.                         ;The following comparisons see
  491.                         ;If we have a valid buffer..
  492.         cmp    word ptr StdGrp:FILES[si].fbuffptr + 2, NIL
  493.         jne    fputc_do_this
  494.         cmp    word ptr StdGrp:FILES[si].fbuffptr, NIL
  495.         je    no_such_thing
  496. fputc_do_this:
  497.         les    di, StdGrp:FILES[si].fbuffptr
  498.         add    di, StdGrp:FILES[si].findex
  499.         mov    es:[di], al        ;Stick that char in the buffer
  500.         inc    byte ptr StdGrp:FILES[si].findex
  501.         jne    fwrite_done        ;If we hit 0, write the buffer!
  502.         lds    dx, StdGrp:FILES[si].fbuffptr
  503.         mov    bx, StdGrp:FILES[si].fhandle
  504.         mov    cx, 256
  505.         mov    ah, 40h
  506.         int     21h
  507. ;
  508. fwrite_done:
  509.         pop    bx
  510.         pop    cx
  511.         pop    dx
  512.         pop    ds
  513.         pop    di
  514.         pop    si
  515.         ret
  516. no_such_thing:    stc
  517.         mov    ax, 6        ;invalid filehandle
  518.                     ;(Safe guard - shouldn't happen)
  519.         jmp    fwrite_done
  520. sl_fputc    endp
  521.  
  522. ;
  523. ;
  524. ;---------------------------------------------------------------------------
  525. ;    Writeoff -> A procedure that resets the stdlib functions of character
  526. ;          and string output.
  527. ;
  528.         public  sl_fwriteOff
  529. sl_fwriteOff    proc    far
  530.         push    es
  531.         push    di
  532.         mov     es, word ptr StdGrp:Old_out_addrss + 2
  533.         mov     di, word ptr StdGrp:Old_out_addrss
  534.         call    sl_SetOutAdrs
  535.         mov    word ptr StdGrp:Old_out_addrss + 2, 0
  536.         mov    word ptr StdGrp:Old_out_addrss, 0
  537.         pop     di
  538.         pop     es
  539.         ret
  540. sl_fwriteOff    endp
  541. ;
  542. ;
  543. ;
  544. ;
  545. ;------------------------------------------------------------------------
  546. ;  Fseek -> Moves the File Pointer a specified # of Bytes..
  547. ;       INPUT-> AL contains the offset from where to start the file seeking.
  548. ;               AL=0;  Seek from the begining of the file.
  549. ;               AL=1;  Seek from the current pointer possition.
  550. ;               AL=2;  Seek backwards from the end of the file.
  551. ;       INPUT-> SI contains the Unique FileHandle given by these procedures.
  552. ;       INPUT-> CX:DX contains the number of bytes to seek (Unsigned number).
  553. ;       OUTPUT -> Carry flag is set if error..
  554. ;       OUTPUT -> AX contains the error code.
  555. ;       OUTPUT -> DX:AX contains the new file positoin if no error.
  556. ;
  557.         public  sl_fseek
  558. sl_fseek        proc    far
  559.         push    bx
  560.         mov     ah, 42h
  561.         mov     bx, StdGrp:FILES[si].fhandle   ;Get the FileHandle
  562.         int     21h
  563.         pop     bx
  564. sl_fseek        endp
  565. ;
  566. ;-----------------------------------------------------------------------
  567. ;  DOSHandle -> A procedure that returns the DOS given FileHandle.
  568. ;       INPUT-> AX contains the stdlib given FileHandle.
  569. ;       OUTPUT->Carry is set if error.
  570. ;       OUTPUT->AX contains error code.
  571. ;               AX=1; Invalid user input FileIndex.
  572. ;       OUTPUT->AX contains the DOS FileHandle.
  573. ;
  574.         public  sl_DOSHandle
  575. sl_DOSHandle    proc    far
  576.         push    si
  577.         mov     si, ax
  578.         cmp     si, length FILES * size ftype
  579.         ja      Handle_error
  580.         cmp     si, 0
  581.         jb      Handle_error
  582.         clc
  583.         mov     ax, StdGrp:FILES[si].fhandle
  584.         jmp     DOS_Done
  585. Handle_error:   stc
  586.         mov     ax, 1
  587. DOS_Done:
  588.         pop     si
  589.         ret
  590. sl_DOSHandle    endp
  591. ;
  592. ;--------------------------------------------------------------------------
  593. ;  Fdel -> A procedure that deletes a file.
  594. ;    INPUT->ES:DI contains the address of zero terminated pathname of file.
  595. ;    OUTPUT -> CARRY Flag is set if error and
  596. ;          AX=2 File not found
  597. ;          AX=5 Access denied
  598. ;
  599.         public    sl_fdel
  600. sl_fdel        proc    far
  601.         push    ds
  602.         push    dx
  603.         mov    dx, es
  604.         mov    ds, dx
  605.         mov    dx, di
  606.         mov    ah, 41h
  607.         int     21h
  608.         pop    dx
  609.         pop    ds
  610.         ret
  611. sl_fdel        endp
  612. ;
  613. ;--------------------------------------------------------------------------
  614. ;  Frename -> A procedure that renames one file to another.
  615. ;    INPUT -> DX:SI contains the origonal pathname of the file.
  616. ;    INPUT -> ES:DI contains the new pathname of the file.
  617. ;    OUTPUT -> CARRY Flag is set if error and:
  618. ;          AX=2  File not found
  619. ;          AX=5  Access denied
  620. ;          AX=17 Not the Same device
  621. ;
  622.         public    sl_frename
  623. sl_frename    proc    far
  624.         push    ds
  625.         mov    ds, dx
  626.         mov    dx, si
  627.         mov    ah, 56h
  628.         int     21h
  629.         pop    ds
  630.         ret
  631. sl_frename    endp
  632. ;
  633. ;
  634. stdlib          ends
  635. ;
  636. ;
  637.         end
  638.  
  639.